package com.eric.statemachinedemo.config;

import com.eric.statemachinedemo.constant.RmaEventEnum;
import com.eric.statemachinedemo.constant.RmaStatusEnum;
import com.eric.statemachinedemo.model.request.ProcessRequestData;
import com.eric.statemachinedemo.strategy.RmaProcessExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.statemachine.support.DefaultStateMachineContext;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;


@Configuration
@EnableStateMachineFactory(name="rmaStateMachineFactory")
public class RmaStateMachineConfig extends StateMachineConfigurerAdapter<RmaStatusEnum, RmaEventEnum> {

    //指定id
    public final static String MACHINE_ID = "rmaStateMachine";

    @Override
    public void configure(StateMachineStateConfigurer<RmaStatusEnum, RmaEventEnum> states) throws Exception {
        states
                .withStates()
                .initial(RmaStatusEnum.KEYIN)
                .end(RmaStatusEnum.CLOSED)
                .states(EnumSet.allOf(RmaStatusEnum.class));
    }

    @Bean
    public Action<RmaStatusEnum, RmaEventEnum> action() {
        //定义状态转换的行为
        return new Action<RmaStatusEnum, RmaEventEnum>() {
            @Autowired
            private RmaProcessExecutor processExecutor;

            @Override
            public void execute(StateContext<RmaStatusEnum, RmaEventEnum> context) {
                ProcessRequestData processRequestData = (ProcessRequestData)context.getMessageHeader("processRequestData");
                processExecutor.complete(processRequestData, context.getSource().getId());
            }
        };
    }

    @Bean
    public Action<RmaStatusEnum, RmaEventEnum> errorAction() {
        //定义状态转换出错后的行为
        return context -> {
            context.getStateMachine().setStateMachineError(context.getException());
        };
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<RmaStatusEnum, RmaEventEnum> transitions) throws Exception {
        transitions
                //从keyin到repair
                .withExternal()
                .source(RmaStatusEnum.KEYIN).target(RmaStatusEnum.REPAIR).event(RmaEventEnum.EVENT_KEYIN2REPAIR).action(action(), errorAction())
                .and()
                //从repair到fianl test
                .withExternal()
                .source(RmaStatusEnum.REPAIR).target(RmaStatusEnum.FINALTEST).event(RmaEventEnum.EVENT_REPAIR2TEST).action(action(), errorAction())
                .and()
                //从final test到pack
                .withExternal()
                .source(RmaStatusEnum.FINALTEST).target(RmaStatusEnum.PACK).event(RmaEventEnum.EVENT_TEST2PACK).action(action(), errorAction())
                .and()
                //从final test到repair
                .withExternal()
                .source(RmaStatusEnum.FINALTEST).target(RmaStatusEnum.REPAIR).event(RmaEventEnum.EVENT_TEST2REPAIR).action(action(), errorAction())
                .and()
                //从pack到ship
                .withExternal()
                .source(RmaStatusEnum.PACK).target(RmaStatusEnum.SHIP).event(RmaEventEnum.EVENT_PACK2SHIP).action(action(), errorAction())
                .and()
                //从ship到close
                .withExternal()
                .source(RmaStatusEnum.SHIP).target(RmaStatusEnum.CLOSED).event(RmaEventEnum.EVENT_SHIP2CLOSE).action(action(), errorAction())
                .and();
    }


    /**
     * 持久化配置
     * 实际使用中，可以配合redis+db，进行持久化操作
     * @return
     */
    @Bean("rmaStateMachinePersist")
    public StateMachinePersister<RmaStatusEnum, RmaEventEnum, String> persist() {
        return new DefaultStateMachinePersister<RmaStatusEnum, RmaEventEnum, String>(new StateMachinePersist<RmaStatusEnum, RmaEventEnum, String>() {

            Map<String, RmaStatusEnum> cache = new HashMap<>();

            @Override
            public void write(StateMachineContext<RmaStatusEnum, RmaEventEnum> stateMachineContext, String businessKey) throws Exception {
                //持久化，写入redis+db，或者直接不写
                cache.put(businessKey, stateMachineContext.getState());
            }

            @Override
            public StateMachineContext<RmaStatusEnum, RmaEventEnum> read(String businessKey) throws Exception {
                //读状态机，把当前状态读出来，或者直接读DB里的状态
                return cache.containsKey(businessKey) ?
                        new DefaultStateMachineContext<>(cache.get(businessKey), null, null, null, null, RmaStateMachineConfig.MACHINE_ID) :
                        new DefaultStateMachineContext<>(RmaStatusEnum.KEYIN, null, null, null, null, RmaStateMachineConfig.MACHINE_ID);
            }
        });
    }
}
